package com.srmt.feishu.client.impl;

import com.lark.oapi.Client;
import com.lark.oapi.core.request.RequestOptions;
import com.lark.oapi.core.response.RawResponse;
import com.lark.oapi.core.token.AccessTokenType;
import com.lark.oapi.core.utils.Jsons;
import com.srmt.feishu.client.model.ProtectedDimension;
import com.srmt.feishu.client.model.DimensionRange;
import com.srmt.feishu.client.exception.FeishuApiException;
import com.srmt.feishu.client.util.ResponseValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * Sheets v2 API客户端
 * 使用原生HTTP调用实现飞书SDK中缺失的Sheets v2 API功能
 * 
 * @author SRMT Development Team
 * @since 5.0.0
 */
public class SheetsV2ApiClient {
    
    private static final Logger logger = LoggerFactory.getLogger(SheetsV2ApiClient.class);
    
    private static final String BASE_URL = "https://open.feishu.cn/open-apis/sheets/v2/spreadsheets";
    
    private final Client client;
    
    /**
     * 构造函数
     * 
     * @param client 飞书客户端
     */
    public SheetsV2ApiClient(Client client) {
        if (client == null) {
            throw new IllegalArgumentException("客户端不能为null");
        }
        this.client = client;
    }
    
    // ========== 数据读写操作 ==========
    
    /**
     * 读取单个范围的数据
     * 
     * @param spreadsheetToken 电子表格token
     * @param range 范围（如"Sheet1!A1:B2"）
     * @return 单元格数据
     */
    public List<List<String>> getValues(String spreadsheetToken, String range) throws Exception {
        try {
            // 构建URL
            String encodedRange = URLEncoder.encode(range, StandardCharsets.UTF_8.toString());
            String url = String.format("%s/%s/values/%s", BASE_URL, spreadsheetToken, encodedRange);
            
            // 添加查询参数
            url += "?valueRenderOption=ToString&dateTimeRenderOption=FormattedString";
            
            logger.info("从URL获取值: {}", url);
            
            // 发起GET请求
            RequestOptions options = RequestOptions.newBuilder()
                    .build();
            
            RawResponse response = client.get(url, null, AccessTokenType.Tenant, options);

            // 使用统一的ResponseValidator解析响应
            return ResponseValidator.validateAndExtractFromRaw(
                response,
                "getValues",
                responseMap -> {
                    Map<String, Object> data = (Map<String, Object>) responseMap.get("data");
                    if (data != null) {
                        Map<String, Object> valueRange = (Map<String, Object>) data.get("valueRange");
                        if (valueRange != null) {
                            List<List<Object>> values = (List<List<Object>>) valueRange.get("values");
                            if (values != null) {
                                // 转换为String类型
                                List<List<String>> result = new ArrayList<>();
                                for (List<Object> row : values) {
                                    List<String> stringRow = new ArrayList<>();
                                    for (Object cell : row) {
                                        stringRow.add(cell != null ? cell.toString() : "");
                                    }
                                    result.add(stringRow);
                                }
                                return result;
                            }
                        }
                    }
                    // 返回空列表（没有数据但API调用成功）
                    return new ArrayList<>();
                }
            );

        } catch (FeishuApiException e) {
            // 重新抛出FeishuApiException
            throw e;
        } catch (Exception e) {
            logger.error("获取数据失败,范围 {}: {}", range, e.getMessage());
            throw new FeishuApiException("getValues", -1,
                "获取数据失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 更新单个范围的数据
     * 
     * @param spreadsheetToken 电子表格token
     * @param range 范围
     * @param values 数据
     * @return 是否成功
     */
    public boolean updateValues(String spreadsheetToken, String range, List<List<String>> values) throws Exception {
        try {
            String url = String.format("%s/%s/values?valueRenderOption=ToString", BASE_URL, spreadsheetToken);
            
            // 构建请求体
            Map<String, Object> body = new HashMap<>();
            Map<String, Object> valueRange = new HashMap<>();
            valueRange.put("range", range);
            valueRange.put("values", values);
            body.put("valueRange", valueRange);
            
            logger.info("更新 values at range {}: {}", range, values);
            
            // 发起PUT请求
            RequestOptions options = RequestOptions.newBuilder()
                    .build();
            
            RawResponse response = client.put(url, body, AccessTokenType.Tenant, options);

            // 使用统一的ResponseValidator检查响应
            ResponseValidator.validateRawResponse(response, "updateValues");
            return true;

        } catch (FeishuApiException e) {
            // 重新抛出FeishuApiException
            throw e;
        } catch (Exception e) {
            logger.error("更新数据失败: {}", e.getMessage(), e);
            throw new FeishuApiException("updateValues", -1,
                "更新数据失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 批量更新多个范围的数据
     * 
     * @param spreadsheetToken 电子表格token
     * @param updates 多个范围的更新数据
     * @return 是否成功
     */
    public boolean batchUpdateValues(String spreadsheetToken, Map<String, List<List<String>>> updates) throws Exception {
        try {
            String url = String.format("%s/%s/values_batch_update", BASE_URL, spreadsheetToken);
            
            // 构建请求体
            Map<String, Object> body = new HashMap<>();
            List<Map<String, Object>> valueRanges = new ArrayList<>();
            
            for (Map.Entry<String, List<List<String>>> entry : updates.entrySet()) {
                Map<String, Object> valueRange = new HashMap<>();
                valueRange.put("range", entry.getKey());
                valueRange.put("values", entry.getValue());
                valueRanges.add(valueRange);
            }
            
            body.put("valueRanges", valueRanges);
            
            logger.debug("批量更新{}个范围", updates.size());
            
            // 发起POST请求
            RawResponse response = client.post(url, body, AccessTokenType.Tenant);

            // 使用统一的ResponseValidator检查响应
            ResponseValidator.validateRawResponse(response, "batchUpdateValues");
            return true;

        } catch (FeishuApiException e) {
            // 重新抛出FeishuApiException
            throw e;
        } catch (Exception e) {
            logger.error("批量更新数据失败: {}", e.getMessage());
            throw new FeishuApiException("batchUpdateValues", -1,
                "批量更新数据失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 在指定范围上方插入数据（会新增行）
     * 
     * @param spreadsheetToken 电子表格token
     * @param range 范围
     * @param values 数据
     * @return 是否成功
     */
    public boolean prependValues(String spreadsheetToken, String range, List<List<String>> values) throws Exception {
        try {
            String url = String.format("%s/%s/values_prepend?valueRenderOption=ToString", BASE_URL, spreadsheetToken);
            
            // 构建请求体
            Map<String, Object> body = new HashMap<>();
            Map<String, Object> valueRange = new HashMap<>();
            valueRange.put("range", range);
            valueRange.put("values", values);
            body.put("valueRange", valueRange);
            
            logger.debug("在范围{}前置插入数据", range);
            
            // 发起POST请求
            RawResponse response = client.post(url, body, AccessTokenType.Tenant);

            // 使用统一的ResponseValidator检查响应
            ResponseValidator.validateRawResponse(response, "prependValues");
            return true;

        } catch (FeishuApiException e) {
            // 重新抛出FeishuApiException
            throw e;
        } catch (Exception e) {
            logger.error("前置添加数据失败: {}", e.getMessage());
            throw new FeishuApiException("prependValues", -1,
                "前置插入数据失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 追加数据（通过组合操作实现）
     *
     * @param spreadsheetToken 电子表格token
     * @param sheetId 工作表ID
     * @param values 数据
     * @return 是否成功
     * @throws FeishuApiException 如果API调用失败
     */
    public boolean appendValues(String spreadsheetToken, String sheetId, List<List<String>> values) throws Exception {
        try {
            // 由于没有直接的append API，需要组合使用：
            // 1. 先增加足够的行
            // 2. 然后写入数据

            logger.info("追加 values using combination of addDimensionRange and updateValues");

            // Step 1: 增加行（如果失败会直接抛出异常）
            int rowCount = values.size();
            addDimensionRange(spreadsheetToken, sheetId, "ROWS", rowCount);

            // Step 2: 写入数据到新增的行
            // 这里需要知道当前表格的行数，暂时使用一个较大的起始行号
            // 实际使用时应该先查询表格信息获取准确的行数
            String range = String.format("%s!A1000:Z%d", sheetId, 1000 + rowCount);
            return updateValues(spreadsheetToken, range, values);

        } catch (FeishuApiException e) {
            // 重新抛出FeishuApiException
            throw e;
        } catch (Exception e) {
            logger.error("追加数据失败: {}", e.getMessage());
            throw new FeishuApiException("appendValues", -1,
                "追加数据失败: " + e.getMessage(), e);
        }
    }
    
    // ========== 维度操作 ==========
    
    /**
     * 增加空白行或列
     * 
     * @param spreadsheetToken 电子表格token
     * @param sheetId 工作表ID
     * @param dimension 维度（ROWS或COLUMNS）
     * @param length 长度
     * @return 是否成功
     */
    public boolean addDimensionRange(String spreadsheetToken, String sheetId, String dimension, int length) throws Exception {
        try {
            String url = String.format("%s/%s/dimension_range", BASE_URL, spreadsheetToken);
            
            // 构建请求体
            Map<String, Object> body = new HashMap<>();
            Map<String, Object> dimensionObj = new HashMap<>();
            dimensionObj.put("sheetId", sheetId);
            dimensionObj.put("majorDimension", dimension);
            dimensionObj.put("length", length);
            body.put("dimension", dimensionObj);
            
            logger.debug("向工作表{}添加{}个{}", sheetId, length, dimension);
            
            // 发起POST请求
            RawResponse response = client.post(url, body, AccessTokenType.Tenant);

            // 使用统一的ResponseValidator检查响应
            ResponseValidator.validateRawResponse(response, "addDimensionRange");
            return true;

        } catch (FeishuApiException e) {
            // 重新抛出FeishuApiException
            throw e;
        } catch (Exception e) {
            logger.error("添加维度范围失败: {}", e.getMessage());
            throw new FeishuApiException("addDimensionRange", -1,
                "添加行列失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 在指定位置插入空白行或列
     * 
     * @param spreadsheetToken 电子表格token
     * @param sheetId 工作表ID
     * @param dimension 维度（ROWS或COLUMNS）
     * @param startIndex 起始索引
     * @param endIndex 结束索引
     * @return 是否成功
     */
    public boolean insertDimensionRange(String spreadsheetToken, String sheetId, String dimension,
                                      int startIndex, int endIndex) throws Exception {
        try {
            String url = String.format("%s/%s/insert_dimension_range", BASE_URL, spreadsheetToken);
            
            // 构建请求体
            Map<String, Object> body = new HashMap<>();
            Map<String, Object> dimensionObj = new HashMap<>();
            dimensionObj.put("sheetId", sheetId);
            dimensionObj.put("majorDimension", dimension);
            dimensionObj.put("startIndex", startIndex);
            dimensionObj.put("endIndex", endIndex);
            body.put("dimension", dimensionObj);
            body.put("inheritStyle", "BEFORE");
            
            logger.debug("插入 {} from {} to {} in sheet {}", dimension, startIndex, endIndex, sheetId);
            
            // 发起POST请求
            RawResponse response = client.post(url, body, AccessTokenType.Tenant);

            // 使用统一的ResponseValidator检查响应
            ResponseValidator.validateRawResponse(response, "insertDimensionRange");
            return true;

        } catch (FeishuApiException e) {
            // 重新抛出FeishuApiException
            throw e;
        } catch (Exception e) {
            logger.error("插入维度范围失败: {}", e.getMessage());
            throw new FeishuApiException("insertDimensionRange", -1,
                "插入行列失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 删除指定范围的行或列
     * 
     * @param spreadsheetToken 电子表格token
     * @param sheetId 工作表ID
     * @param dimension 维度（ROWS或COLUMNS）
     * @param startIndex 起始索引
     * @param endIndex 结束索引
     * @return 是否成功
     */
    public boolean deleteDimensionRange(String spreadsheetToken, String sheetId, String dimension,
                                       int startIndex, int endIndex) throws Exception {
        try {
            String url = String.format("%s/%s/dimension_range", BASE_URL, spreadsheetToken);
            
            // 构建请求体
            Map<String, Object> body = new HashMap<>();
            Map<String, Object> dimensionObj = new HashMap<>();
            dimensionObj.put("sheetId", sheetId);
            dimensionObj.put("majorDimension", dimension);
            dimensionObj.put("startIndex", startIndex);
            dimensionObj.put("endIndex", endIndex);
            body.put("dimension", dimensionObj);
            
            logger.debug("删除 {} from {} to {} in sheet {}", dimension, startIndex, endIndex, sheetId);
            
            // 发起DELETE请求
            RequestOptions options = RequestOptions.newBuilder()
                    .build();
            
            RawResponse response = client.delete(url, body, AccessTokenType.Tenant, options);

            // 使用统一的ResponseValidator检查响应
            ResponseValidator.validateRawResponse(response, "deleteDimensionRange");
            return true;

        } catch (FeishuApiException e) {
            // 重新抛出FeishuApiException
            throw e;
        } catch (Exception e) {
            logger.error("删除维度范围失败: {}", e.getMessage());
            throw new FeishuApiException("deleteDimensionRange", -1,
                "删除行列失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 更新行高或列宽（暂不支持）
     * 
     * @throws UnsupportedOperationException 暂不支持此操作
     */
    public boolean updateDimensionRange(String spreadsheetToken, String sheetId, String dimension,
                                       int startIndex, int endIndex, int size) throws Exception {
        throw new UnsupportedOperationException("更新 dimension range (row height/column width) is not yet supported by Feishu API");
    }
    
    // ========== 单元格操作 ==========
    
    /**
     * 合并单元格
     * 
     * @param spreadsheetToken 电子表格token
     * @param range 范围（如"Sheet1!A1:B2"）
     * @param mergeType 合并类型（MERGE_ALL, MERGE_ROWS, MERGE_COLUMNS）
     * @return 是否成功
     */
    public boolean mergeCells(String spreadsheetToken, String range, String mergeType) throws Exception {
        try {
            String url = String.format("%s/%s/merge_cells", BASE_URL, spreadsheetToken);
            
            // 构建请求体
            Map<String, Object> body = new HashMap<>();
            body.put("range", range);
            body.put("mergeType", mergeType != null ? mergeType : "MERGE_ALL");
            
            logger.debug("合并范围{}中的单元格,类型为{}", range, mergeType);
            
            // 发起POST请求
            RawResponse response = client.post(url, body, AccessTokenType.Tenant);

            // 使用统一的ResponseValidator检查响应
            ResponseValidator.validateRawResponse(response, "mergeCells");
            return true;

        } catch (FeishuApiException e) {
            // 重新抛出FeishuApiException
            throw e;
        } catch (Exception e) {
            logger.error("合并单元格失败: {}", e.getMessage());
            throw new FeishuApiException("mergeCells", -1,
                "合并单元格失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 取消合并单元格
     * 
     * @param spreadsheetToken 电子表格token
     * @param range 范围
     * @return 是否成功
     */
    public boolean unmergeCells(String spreadsheetToken, String range) throws Exception {
        try {
            String url = String.format("%s/%s/unmerge_cells", BASE_URL, spreadsheetToken);
            
            // 构建请求体
            Map<String, Object> body = new HashMap<>();
            body.put("range", range);
            
            logger.debug("取消合并范围{}内的单元格", range);
            
            // 发起POST请求
            RawResponse response = client.post(url, body, AccessTokenType.Tenant);

            // 使用统一的ResponseValidator检查响应
            ResponseValidator.validateRawResponse(response, "unmergeCells");
            return true;

        } catch (FeishuApiException e) {
            // 重新抛出FeishuApiException
            throw e;
        } catch (Exception e) {
            logger.error("取消合并单元格失败: {}", e.getMessage());
            throw new FeishuApiException("unmergeCells", -1,
                "取消合并单元格失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 设置单元格样式（暂不支持）
     * 
     * @throws UnsupportedOperationException 暂不支持此操作
     */
    public boolean setCellStyle(String spreadsheetToken, String range, Object style) throws Exception {
        throw new UnsupportedOperationException("设置 cell style is not yet supported");
    }
    
    /**
     * 设置数据验证（暂不支持）
     * 
     * @throws UnsupportedOperationException 暂不支持此操作
     */
    public boolean setDataValidation(String spreadsheetToken, String range, Object validation) throws Exception {
        throw new UnsupportedOperationException("设置 data validation is not yet supported");
    }
    
    // ========== 保护范围操作 ==========
    
    /**
     * 添加保护范围
     * 
     * @param spreadsheetToken 电子表格token
     * @param protectedDimensions 保护维度配置列表
     * @param userIdType 用户ID类型
     * @return 是否成功
     */
    public boolean addProtectedDimension(String spreadsheetToken, 
                                        List<ProtectedDimension> protectedDimensions,
                                        String userIdType) throws Exception {
        try {
            String url = String.format("%s/%s/protected_dimension", BASE_URL, spreadsheetToken);
            if (userIdType != null && !userIdType.isEmpty()) {
                url += "?user_id_type=" + userIdType;
            }
            
            // 转换为API格式
            List<Map<String, Object>> apiDimensions = new ArrayList<>();
            for (ProtectedDimension pd : protectedDimensions) {
                Map<String, Object> apiDim = new HashMap<>();
                
                // 构建dimension对象
                Map<String, Object> dimMap = new HashMap<>();
                dimMap.put("sheetId", pd.getDimension().getSheetId());
                dimMap.put("majorDimension", pd.getDimension().getMajorDimension().getValue());
                dimMap.put("startIndex", pd.getDimension().getStartIndex());
                dimMap.put("endIndex", pd.getDimension().getEndIndex());
                
                apiDim.put("dimension", dimMap);
                apiDim.put("users", pd.getUsers());
                apiDim.put("lockInfo", pd.getLockInfo());
                
                apiDimensions.add(apiDim);
            }
            
            Map<String, Object> body = new HashMap<>();
            body.put("addProtectedDimension", apiDimensions);
            
            logger.info("向电子表格添加{}个保护维度: {}", 
                       protectedDimensions.size(), spreadsheetToken);
            
            // 发起POST请求
            RawResponse response = client.post(url, body, AccessTokenType.Tenant);

            // 使用统一的ResponseValidator检查响应
            ResponseValidator.validateRawResponse(response, "addProtectedDimension");
            return true;

        } catch (FeishuApiException e) {
            // 重新抛出FeishuApiException
            throw e;
        } catch (Exception e) {
            logger.error("添加保护维度失败: {}", e.getMessage());
            throw new FeishuApiException("addProtectedDimension", -1,
                "添加保护维度失败: " + e.getMessage(), e);
        }
    }
}